using System;
using System.Collections.Generic;
using System.Reflection;
using Unity.Extensions;
using Xunit;

namespace Unity.Extensions
{
    public class FindMethodInterfaceInterceptorTest
    {
        [Fact]
        public void GenericClassGenericMethod()
        {
            MethodInfo result = InterfaceInterceptor.FindMethod("Insert", new object[] { typeof(int), "T" }, typeof(IList<>).GetMethods());

            Assert.Same(typeof(IList<>).GetMethod("Insert"), result);
        }

        [Fact]
        public void GenericClassGenericMethodWithExtraGenerics()
        {
            MethodInfo result = InterfaceInterceptor.FindMethod("Bar", new object[] { "T", "T2" }, typeof(IFoo<>).GetMethods());

            Assert.Same(typeof(IFoo<>).GetMethod("Bar"), result);
        }

        [Fact]
        public void GenericClassNonGenericMethod()
        {
            MethodInfo result = InterfaceInterceptor.FindMethod("RemoveAt", new[] { typeof(int) }, typeof(IList<>).GetMethods());

            Assert.Same(typeof(IList<>).GetMethod("RemoveAt"), result);
        }

        [Fact]
        public void MethodNotFound()
        {
            MethodInfo result = InterfaceInterceptor.FindMethod("ThisMethodDoesNotExist", new Type[0], typeof(Object).GetMethods());

            Assert.Null(result);
        }

        [Fact]
        public void NonGenericClass()
        {
            MethodInfo result = InterfaceInterceptor.FindMethod("ToString", new Type[0], typeof(Object).GetMethods());

            Assert.Same(typeof(Object).GetMethod("ToString"), result);
        }

        [Fact]
        public void Overloads()
        {
            MethodInfo result1 = InterfaceInterceptor.FindMethod("Overload", new Type[0], typeof(IFoo<>).GetMethods());
            MethodInfo result2 = InterfaceInterceptor.FindMethod("Overload", new[] { typeof(int) }, typeof(IFoo<>).GetMethods());
            MethodInfo result3 = InterfaceInterceptor.FindMethod("Overload", new[] { typeof(double) }, typeof(IFoo<>).GetMethods());

            Assert.NotNull(result1);
            Assert.NotNull(result2);
            Assert.Null(result3);
            Assert.NotSame(result1, result2);
        }

        [Fact]
        public void ParameterTypesNotMatching()
        {
            MethodInfo result = InterfaceInterceptor.FindMethod("ToString", new[] { typeof(int) }, typeof(Object).GetMethods());

            Assert.Null(result);
        }

        public class Foo<T> : IFoo<T>
        {
            public void Bar<T2>(T data,
                                T2 data2) {}

            public void Baz() {}

            public void Overload() {}

            public void Overload(int x) {}
        }

        public interface IFoo<T>
        {
            void Bar<T2>(T data,
                         T2 data2);

            void Overload();
            void Overload(int x);
        }
    }
}